Spring Bean

前言

在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。

Spring中的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢? 例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,如何保证其安全呢? Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每个 JVM 内只有一个实例。

在大多数情况下,单例 bean 是很理想的方案。不过,有时候你可能会发现你所使用的类是易变的,它们会保持一些状态,因此重用是不安全的。在这种情况下,将 class 声明为单例的就不是那么明智了。因为对象会被污染,稍后重用的时候会出现意想不到的问题,所以 Spring 定义了多种作用域的bean。

bean的作用域

创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的“配方”。把bean定义看成一个配方很有意义,它与class很类似,只根据一张“处方”就可以创建多个实例。不仅可以控制注入到对象中的各种依赖和配置值,还可以控制该对象的作用域。这样可以灵活选择所建对象的作用域,而不必在Java Class级定义作用域。

Spring Framework支持五种作用域,分别阐述如下表:

bean1

五种作用域中,request、sessionglobal session 三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于 web 的 Spring ApplicationContext(另外一种IOC容器)环境。

  1. singleton——唯一 bean 实例

    当一个 bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。 singleton 是单例类型(对应于单例模式),就是在创建起容器时就同时自动创建了一个bean的对象不管你是否使用,但我们可以指定Bean节点的 lazy-init=”true” 来延迟初始化bean**,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。 每次获取到的对象都是同一个对象。注意,singleton 作用域是Spring中的缺省作用域。

    要在XML中将 bean 定义成 singleton ,可以这样配置:

    1
    <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

    也可以通过 @Scope 注解(它可以显示指定bean的作用范围)的方式:

    1
    2
    3
    4
    5
    @Service
    @Scope("singleton")
    public class ServiceImpl{

    }
  2. prototype——每次请求都会创建一个新的 bean 实例

    当一个bean的作用域为 prototype,表示一个 bean 定义对应多个对象实例。 prototype 作用域的 bean 会导致在每次对该 bean 请求(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。

    根据经验,对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。

    在 XML 中将 bean 定义成 prototype ,可以这样配置:

    1
    2
    3
    <bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
    或者
    <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>

    通过 @Scope 注解的方式实现就不做演示了。

  3. request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效

    request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。

    在 XML 中将 bean 定义成 request ,可以这样配置:

    1
    <bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
  4. session——每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效

    session只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效.与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的 HTTP session 中根据 userPreferences 创建的实例,将不会看到这些特定于某个 HTTP session 的状态变化。当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉。

    在 XML 中将 bean 定义成 session ,可以这样配置:

    1
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
  5. globalSession

    globalSession作用域类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portle t所共享。在global session 作用域中定义的 bean 被限定于全局portlet Session的生命周期范围内。

    在 XML 中将 bean 定义成 globalSession ,可以这样配置:

    1
    <bean id="user" class="com.foo.Preferences "scope="globalSession"/>

bean的生命周期

概述

Spring的IOC容器功能非常强大,负责Spring的Bean的创建和管理等功能。而Spring bean是整个Spring应用中很重要的一部分,了解Spring Bean的生命周期对我们了解整个Spring框架会有很大的帮助。

BeanFactory、ApplicationContext是Spring两种很重要的容器。前者提供了最基本的依赖注入的支持,而后者在继承前者的基础进行了功能的拓展,例如增加了事件传播,资源访问和国际化的消息访问等功能。本文主要介绍了ApplicationContext和BeanFactory两种容器的Bean的生命周期。

Spring Bean的生命周期总的来说是这样子的:

bean4

  • Bean容器找到配置文件中 Spring Bean 的定义。
  • Bean容器利用Java Reflection API创建一个Bean的实例
  • 如果涉及到一些属性值 利用set方法设置一些属性值
  • 如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。
  • 如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
  • 如果Bean实现了BeanFactoryAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
  • 与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。
  • 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法
  • 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。
  • 如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。
  • 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessAfterInitialization()方法
  • 当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。
  • 当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。

ApplicationContext Bean生命周期

bean2

上图修正为“调用disposableBean的destroy()方法”

ApplicationContext容器中,Bean的生命周期流程如上图所示,流程大致如下:

1.首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化。

2.按照Bean定义信息配置信息,注入所有的属性。

3.如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该Bean就获得了自己在配置文件中的id。

4.如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory。

5.如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext。

6.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法。

7.如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法。

8.如果Bean配置了init-method方法,则会执行init-method配置的方法。

9.如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法。

10.经过流程9之后,就可以正式使用该Bean了。对于scope为singleton的Bean,Spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,其生命周期就交给调用方管理了,不再是Spring容器进行管理了。

11.容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法。

12.如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法。至此,整个Bean的生命周期结束。

示例:

我们定义了一个Person类,该类实现了BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean五个接口。在applicationContext.xml文件中配置了该Bean的id为person1,并且配置了init-method和destroy-method,为该Bean配置了属性name为jack的值,然后定义了一个MyBeanPostProcessor方法,该方法实现了BeanPostProcessor接口,在applicationContext.xml文件中配置了该方法的Bean,其代码如下所示:

applicationContext.xml文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<bean id="person1" destroy-method="myDestroy"
init-method="myInit" class="com.test.spring.life.Person">
<property name="name">
<value>jack</value>
</property>
</bean>

<!-- 配置自定义的后置处理器 -->
<bean id="postProcessor" class="com.pingan.spring.life.MyBeanPostProcessor" />
</beans>

Person类,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class Person implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, InitializingBean, DisposableBean {

private String name;

public Person() {
System.out.println("PersonService类构造方法");
}


public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("set方法被调用");
}

//自定义的初始化函数
public void myInit() {
System.out.println("myInit被调用");
}

//自定义的销毁方法
public void myDestroy() {
System.out.println("myDestroy被调用");
}

public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("destory被调用");
}

public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("afterPropertiesSet被调用");
}

public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("setApplicationContext被调用");
}

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("setBeanFactory被调用,beanFactory");
}

public void setBeanName(String beanName) {
// TODO Auto-generated method stub
System.out.println("setBeanName被调用,beanName:" + beanName);
}

public String toString() {
return "name is :" + name;
}

MyBeanPostProcessor类,实现了BeanPostProcessor接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyBeanPostProcessor implements BeanPostProcessor {

public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
// TODO Auto-generated method stub

System.out.println("postProcessBeforeInitialization被调用");
return bean;
}

public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization被调用");
return bean;
}

}

测试类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AcPersonServiceTest {

public static void main(String[] args) {
// TODO Auto-generated method stub

System.out.println("开始初始化容器");
ApplicationContext ac = new ClassPathXmlApplicationContext("com/test/spring/life/applicationContext.xml");

System.out.println("xml加载完毕");
Person person1 = (Person) ac.getBean("person1");
System.out.println(person1);
System.out.println("关闭容器");
((ClassPathXmlApplicationContext)ac).close();

}

}

我们启动容器,可以看到整个初始化的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
开始初始化容器
九月 25, 2016 10:44:50 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy
九月 25, 2016 10:44:50 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/test/spring/life/applicationContext.xml]
Person类构造方法
set方法被调用
setBeanName被调用,beanName:person1
setBeanFactory被调用,beanFactory
setApplicationContext被调用
postProcessBeforeInitialization被调用
afterPropertiesSet被调用
myInit被调用
postProcessAfterInitialization被调用
xml加载完毕
name is :jack
关闭容器
九月 25, 2016 10:44:51 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy
destory被调用
myDestroy被调用

BeanFactory Bean生命周期

bean3

上图修正为“调用disposableBean的destroy()方法”

BeanFactoty容器中, Bean的生命周期如上图所示,与ApplicationContext相比,有如下几点不同:

1.BeanFactory容器中,不会调用ApplicationContextAware接口的setApplicationContext()方法

2.BeanPostProcessor接口的postProcessBeforeInitialzation()方法和postProcessAfterInitialization()方法不会自动调用,必须自己通过代码手动注册。

3.BeanFactory容器启动的时候,不会去实例化所有Bean,包括所有scope为singleton且非懒加载的Bean也是一样,而是在调用的时候去实例化。

示例:

我们还是使用前面定义好的Person类和MyBeanPostProcessor类,以及ApplicationContext.xml文件。

BfPersonServiceTest测试类的main函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BfPersonServiceTest {

public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("开始初始化容器");
ConfigurableBeanFactory bf = new XmlBeanFactory(new ClassPathResource("com/pingan/spring/life/applicationContext.xml"));
System.out.println("xml加载完毕");
//beanFactory需要手动注册beanPostProcessor类的方法
bf.addBeanPostProcessor(new MyBeanPostProcessor());
Person person1 = (Person) bf.getBean("person1");
System.out.println(person1);
System.out.println("关闭容器");
bf.destroySingletons();
}

}

启动容器,我们可以看到整个调用流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
开始初始化容器
九月 26, 2016 12:27:05 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/pingan/spring/life/applicationContext.xml]
xml加载完毕
PersonService类构造方法
set方法被调用
setBeanName被调用,beanName:person1
setBeanFactory被调用,beanFactory
postProcessBeforeInitialization被调用
afterPropertiesSet被调用
myInit被调用
postProcessAfterInitialization被调用
name is :jack
关闭容器
destory被调用
myDestroy被调用

initialization 和 destroy

有时我们需要在Bean属性值set好之后和Bean销毁之前做一些事情,比如检查Bean中某个属性是否被正常的设置好值了。Spring框架提供了多种方法让我们可以在Spring Bean的生命周期中执行initialization和pre-destroy方法。

  1. 实现InitializingBean和DisposableBean接口

    这两个接口都只包含一个方法。

    通过实现InitializingBean接口的afterPropertiesSet()方法可以在Bean属性值设置好之后做一些操作,实现DisposableBean接口的destroy()方法可以在销毁Bean之前做一些操作。

    例子如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class GiraffeService implements InitializingBean,DisposableBean {
    @Override
    public void afterPropertiesSet() throws Exception {
    System.out.println("执行InitializingBean接口的afterPropertiesSet方法");
    }
    @Override
    public void destroy() throws Exception {
    System.out.println("执行DisposableBean接口的destroy方法");
    }
    }

    注意:这种方法比较简单,但是不建议使用。因为这样会将Bean的实现和Spring框架耦合在一起。

  2. 在bean的配置文件中指定init-method和destroy-method方法

    Spring允许我们创建自己的 init 方法和 destroy 方法,只要在 Bean 的配置文件中指定 init-method 和 destroy-method 的值就可以在 Bean 初始化时和销毁之前执行一些操作

    例子如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class GiraffeService {
    //通过<bean>的destroy-method属性指定的销毁方法
    public void destroyMethod() throws Exception {
    System.out.println("执行配置的destroy-method");
    }
    //通过<bean>的init-method属性指定的初始化方法
    public void initMethod() throws Exception {
    System.out.println("执行配置的init-method");
    }
    }

    配置文件中的配置:

    1
    2
    <bean name="giraffeService" class="com.giraffe.spring.service.GiraffeService" init-method="initMethod" destroy-method="destroyMethod">
    </bean>

    注意:自定义的init-method和post-method方法可以抛异常但是不能有参数。这种方式比较推荐,因为可以自己创建方法,无需将Bean的实现直接依赖于spring的框架。

  3. 使用@PostConstruct和@PreDestroy注解

    除了xml配置的方式,Spring 也支持用 @PostConstruct@PreDestroy注解来指定 initdestroy 方法。这两个注解均在javax.annotation 包中。为了注解可以生效,需要在配置文件中定义org.springframework.context.annotation.CommonAnnotationBeanPostProcessor或context:annotation-config

    例子如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class GiraffeService {
    @PostConstruct
    public void initPostConstruct(){
    System.out.println("执行PostConstruct注解标注的方法");
    }
    @PreDestroy
    public void preDestroy(){
    System.out.println("执行preDestroy注解标注的方法");
    }
    }

    配置文件中的配置:

    1
    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />

实现*Aware接口,在Bean中使用Spring框架的一些对象

有些时候我们需要在 Bean 的初始化中使用 Spring 框架自身的一些对象来执行一些操作。比如获ServletContext 的一些参数,获取 ApplicaitionContext 中的 BeanDefinition 的名字,获取 Bean 在容器中的名字等等。为了让 Bean可以获取到框架自身的一些对象,Spring 提供了一组名为*Aware的接口。

这些接口均继承于org.springframework.beans.factory.Aware标记接口,并提供一个将由 Bean 实现的set*方法,Spring通过基于setter的依赖注入方式使相应的对象可以被Bean使用。

介绍一些重要的Aware接口:

  • ApplicationContextAware: 获得ApplicationContext对象,可以用来获取所有Bean definition的名字。
  • BeanFactoryAware:获得BeanFactory对象,可以用来检测Bean的作用域。
  • BeanNameAware:获得Bean在配置文件中定义的名字。
  • ResourceLoaderAware:获得ResourceLoader对象,可以获得classpath中某个文件。
  • ServletContextAware:在一个MVC应用中可以获取ServletContext对象,可以读取context中的参数。
  • ServletConfigAware: 在一个MVC应用中可以获取ServletConfig对象,可以读取config中的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class GiraffeService implements   ApplicationContextAware,
ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" + beanFactory.isSingleton("giraffeService"));
}
@Override
public void setBeanName(String s) {
System.out.println("执行setBeanName:: Bean Name defined in context="
+ s);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("执行setApplicationContext:: Bean Definition Names="
+ Arrays.toString(applicationContext.getBeanDefinitionNames()));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
System.out.println("执行setApplicationEventPublisher");
}
@Override
public void setEnvironment(Environment environment) {
System.out.println("执行setEnvironment");
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
Resource resource = resourceLoader.getResource("classpath:spring-beans.xml");
System.out.println("执行setResourceLoader:: Resource File Name="
+ resource.getFilename());
}
@Override
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
System.out.println("执行setImportMetadata");
}
}

BeanPostProcessor

上面的*Aware接口是针对某个实现这些接口的Bean定制初始化的过程, Spring同样可以针对容器中的所有Bean,或者某些Bean定制初始化过程,只需提供一个实现BeanPostProcessor接口的类即可。 该接口中包含两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法会在容器中的Bean初始化之前执行, postProcessAfterInitialization方法在容器中的Bean初始化之后执行。

例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class CustomerBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName);
return bean;
}
}

要将BeanPostProcessor的Bean像其他Bean一样定义在配置文件中:

1
<bean class="com.giraffe.spring.service.CustomerBeanPostProcessor"/>

单例管理的对象和非单例管理的对象

单例管理的对象

当scope=”singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init=”true”来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。如下配置:

1
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>

如果想对所有的默认单例bean都应用延迟初始化,可以在根节点beans设置default-lazy-init属性为true,如下所示:

1
<beans default-lazy-init="true" …>

默认情况下,Spring 在读取 xml 文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用 init-method 属性值中所指定的方法。在对象在被销毁的时候,会调用 destroy-method 属性值中所指定的方法。

非单例管理的对象

scope=”prototype”时,容器也会延迟初始化 bean,Spring 读取xml 文件的时候,并不会立刻创建对象,而是在第一次请求该 bean 时才初始化(如调用getBean方法时)。在第一次请求每一个 prototype 的bean 时,Spring容器都会调用其构造器创建这个对象,然后调用init-method属性值中所指定的方法。在对象被销毁的时候,Spring 容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring容器一旦把这个对象交给你之后,就不再管理这个对象了。

总结

可以发现,对于作用域为 prototype 的 bean ,其destroy方法并没有被调用如果 bean 的 scope 设为prototype时,当容器关闭时,destroy 方法不会被调用。

对于 prototype 作用域的 bean,有一点非常重要,那就是 Spring不能对一个 prototype bean 的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。

不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责(让Spring容器释放被prototype作用域bean占用资源的一种可行方式是,通过使用Bean的后置处理器BeanPostProcessor,该处理器持有要被清除的bean的引用)。

谈及prototype作用域的bean时,在某些方面你可以将Spring容器的角色看作是Java new操作的替代者,任何迟于该时间点的生命周期事宜都得交由客户端来处理。

Spring 容器可以管理 singleton 作用域下 bean 的生命周期,在此作用域下,Spring 能够精确地知道bean何时被创建,何时初始化完成,以及何时被销毁。而对于 prototype 作用域的bean,Spring只负责创建,当容器创建了 bean 的实例后,bean 的实例就交给了客户端的代码管理,Spring容器将不再跟踪其生命周期,并且不会管理那些被配置成prototype作用域的bean的生命周期。

参考:

https://github.com/Snailclimb/JavaGuide/blob/master/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6/SpringBean.md

https://www.jianshu.com/p/3944792a5fff

秉持初心,继续向前。
显示 Gitment 评论